package dao

import (
	"context"
	"errors"
	"fmt"
	"ruoyi-go-echo-ddd/internal/domain/model"
	"ruoyi-go-echo-ddd/internal/infrastructure/http/request"
	"ruoyi-go-echo-ddd/internal/infrastructure/persistence/config"
	"ruoyi-go-echo-ddd/internal/infrastructure/utils"

	"github.com/rs/zerolog"
	"gorm.io/gorm"
)

type SysDeptDaoImpl struct {
	db      config.PrimaryDB
	RoleDao *SysRoleDaoImpl
}

func NewSysDeptDaoImpl(db config.PrimaryDB, roleDao *SysRoleDaoImpl) *SysDeptDaoImpl {
	return &SysDeptDaoImpl{
		db:      db,
		RoleDao: roleDao,
	}
}

//goland:noinspection SqlResolve,SqlCaseVsIf,SqlNoDataSourceInspection,SqlDialectInspection
func (orm *SysDeptDaoImpl) Save(ctx context.Context, dept *model.Dept) (*model.Dept, error) {
	exist, err := orm.CheckDeptByName(ctx, dept.DeptName)
	if err != nil {
		return nil, err
	}
	if exist {
		return nil, fmt.Errorf("%s已存在，请勿重复添加", dept.DeptName)
	}

	dept.DelFlag = "0"
	// 如果主键字段默认不是id，调用create需要使用Omit("主键ID")忽略
	result := orm.db.WithContext(ctx).
		//Model(&domain.Dept{}).
		//Omit("dept_id").
		Create(&dept)
	if result.Error != nil {
		return nil, errors.New("部门新增失败")
	}
	deptNew, err := orm.SelectDeptById(ctx, dept.Id)
	if err != nil {
		return nil, errors.New("部门新增失败")
	}
	return deptNew, nil
}

//goland:noinspection SqlResolve,SqlCaseVsIf,SqlNoDataSourceInspection,SqlDialectInspection
func (orm *SysDeptDaoImpl) Update(ctx context.Context, dept *model.Dept) (*model.Dept, error) {
	exist, err := orm.CheckDeptByIdAndName(ctx, dept.Id, dept.DeptName)
	if err != nil {

		return nil, errors.New("部门名称验证失败")
	}

	if exist {
		errExist := fmt.Errorf("部门名称:%s 已存在", dept.DeptName)
		return nil, errExist
	}

	result := orm.db.WithContext(ctx).
		Where("id = ?", dept.Id).
		//Omit("id").
		Save(&dept)
	if result.Error != nil {
		return nil, errors.New("部门修改失败")
	}
	rowsAffected := result.RowsAffected

	var deptNew *model.Dept
	if rowsAffected == 1 {
		deptNew, err = orm.SelectDeptById(ctx, dept.Id)
		if err != nil {
			return nil, errors.New("当前部门不存在")
		}
	}
	return deptNew, nil
}

func (orm *SysDeptDaoImpl) SelectById(ctx context.Context, id int64) (*model.Dept, error) {
	var dept *model.Dept
	if err := orm.db.WithContext(ctx).First(&dept, id).Error; err != nil {
		return nil, err
	}
	return dept, nil
}

//goland:noinspection SqlResolve,SqlCaseVsIf,SqlNoDataSourceInspection,SqlDialectInspection
func (orm *SysDeptDaoImpl) CheckDept(ctx context.Context, deptId int64) (bool, error) {
	var dept *model.Dept

	result := orm.db.WithContext(ctx).Model(&model.Dept{}).
		Where("del_flag", "0").First(&dept, deptId)
	if result.Error == gorm.ErrRecordNotFound {
		return false, errors.New("当前部门不存在")
	}

	if dept.DeptStatus == "1" {
		return false, errors.New("当前部门未启用")
	}
	return true, nil
}

//goland:noinspection SqlResolve,SqlCaseVsIf,SqlNoDataSourceInspection,SqlDialectInspection
func (orm *SysDeptDaoImpl) CheckDeptByName(ctx context.Context, deptName string) (bool, error) {
	var count int64
	if err := orm.db.WithContext(ctx).Model(&model.Dept{}).
		Where("dept_name", deptName).
		Count(&count).Error; err != nil {
		return false, errors.New("通过名称查询部门失败")
	}
	return count > 0, nil
}

//goland:noinspection SqlResolve,SqlCaseVsIf,SqlNoDataSourceInspection,SqlDialectInspection
func (orm *SysDeptDaoImpl) SelectDeptByName(ctx context.Context, deptName string) (*model.Dept, error) {
	var dept *model.Dept
	result := orm.db.WithContext(ctx).Model(&model.Dept{}).Where("dept_name", deptName).First(&dept)
	if result.Error != nil {
		return nil, errors.New("通过名称查询部门失败")
	}
	return dept, nil
}

//goland:noinspection SqlResolve,SqlCaseVsIf,SqlNoDataSourceInspection,SqlDialectInspection
func (orm *SysDeptDaoImpl) SelectDeptById(ctx context.Context, id int64) (*model.Dept, error) {
	var dept *model.Dept
	// 主键设置为id，可以直接使用如下方式查询
	result := orm.db.WithContext(ctx).First(&dept, id)
	if result.Error != nil {
		return nil, errors.New("通过ID查询部门失败")
	}
	return dept, nil
}

func (orm *SysDeptDaoImpl) BatchSave(ctx context.Context, list []*model.Dept) ([]int64, error) {
	if len(list) <= 0 {
		return nil, errors.New("部门批量新增参数验证失败")
	}

	result := orm.db.WithContext(ctx).CreateInBatches(list, 50)
	if result.Error != nil {
		return nil, errors.New("部门批量新增失败")
	}

	var ids []int64

	for _, dept := range list {
		ids = append(ids, dept.Id)
	}

	return ids, nil
}

func (orm *SysDeptDaoImpl) BatchSaveByList(ctx context.Context, list []*request.DeptInsert) ([]int64, error) {
	var ids []int64

	if len(list) <= 0 {
		return nil, errors.New("导入的数据条数必须大于0")
	}

	if err := orm.db.WithContext(ctx).Table("sys_dept").CreateInBatches(&list, 1000).Error; err != nil {
		return nil, errors.New("部门批量新增失败")
	}

	for _, item := range list {
		ids = append(ids, item.Id)
	}

	return ids, nil
}

func (orm *SysDeptDaoImpl) SelectPage(ctx context.Context, param *request.DeptPageParam) ([]*model.Dept, int64, int64, error) {
	pageParam := utils.InitPageParam(param.Page, param.Size)

	// 初始化 GORM 查询
	db := orm.db.WithContext(ctx).Model(&model.Dept{})
	// 动态构建查询条件
	if param.DeptName != "" {
		db.Where(`dept_name ILIKE ?`, "%"+param.DeptName+"%")
	}
	if param.Remarks != "" {
		db.Where(`remarks ILIKE ?`, "%"+param.Remarks+"%")
	}
	if param.Status != "" {
		db.Where("dept_status = ?", param.Status)
	}
	if param.UpdateBy != "" {
		db.Where(`update_by ILIKE ?`, "%"+param.UpdateBy+"%")
	}

	// 查询总记录数
	var total int64
	if err := db.Count(&total).Error; err != nil {
		return nil, 0, 0, errors.New("获取总记录数失败")
	}

	// 查询用户列表
	var list []*model.Dept
	if err := db.
		Offset(int(pageParam.Offset)).Limit(int(pageParam.Size)).
		Order("id, order_num").
		Select("id", "parent_id", "ancestors", "dept_name", "order_num", "dept_status", "create_by",
			"create_time", "update_by", "update_time", "remarks", "del_flag", "leader", "phone", "email").
		Find(&list).Error; err != nil {

		return nil, 0, 0, errors.New("获取用户列表失败")
	}

	if len(list) > 0 {
		var deptIdSlice []int64
		for _, dept := range list {
			if dept != nil && dept.ParentId != 0 {
				deptIdSlice = append(deptIdSlice, dept.ParentId)
			}
		}
		zerolog.DefaultContextLogger.Info().Msgf("没有去重之前的部门ID切片==%s", utils.ToJsonFormat(deptIdSlice))
		deptMap, err := orm.SelectByIds(ctx, utils.Deduplicate(deptIdSlice, func(i int64) int64 { return i }))
		if err != nil {
			return nil, 0, 0, err
		}
		for _, dept := range list {
			dept.ParentName = deptMap[dept.Id]
		}
	}

	return list, total, utils.CalcTotalPage(total, param.Size), nil
}

func (orm *SysDeptDaoImpl) SelectDeptList(ctx context.Context, param *request.DeptPageParam) ([]*model.Dept, error) {
	var list []*model.Dept

	db := orm.db.WithContext(ctx).Model(&model.Dept{}).Where("del_flag = ?", "0")
	// 动态构建查询条件
	if param.DeptName != "" {
		db.Where(`dept_name ILIKE ?`, "%"+param.DeptName+"%")
	}
	if param.Remarks != "" {
		db.Where(`remarks ILIKE ?`, "%"+param.Remarks+"%")
	}
	if param.Status != "" {
		db.Where("dept_status = ?", param.Status)
	}

	if param.UpdateBy != "" {
		db.Where(`update_by ILIKE ?`, "%"+param.UpdateBy+"%")
	}

	if err := db.Order("parent_id, order_num").Find(&list).Error; err != nil {
		zerolog.DefaultContextLogger.Error().Err(err).Msg("查询部门列表错误===")
		return nil, err
	}
	return list, nil
}

func (orm *SysDeptDaoImpl) BatchDelete(ctx context.Context, ids []any) (int64, error) {
	if len(ids) <= 0 {
		return 0, errors.New("部门批量删除参数验证失败")
	}

	// TODO 批量删除操作需要指定Model以便找到表名
	result := orm.db.WithContext(ctx).Model(&model.Dept{}).Where("id IN (?)", ids).Delete(nil)
	if result.Error != nil {
		return 0, errors.New("批量删除失败")
	}
	count := result.RowsAffected
	return count, nil
}

//goland:noinspection SqlResolve,SqlCaseVsIf,SqlNoDataSourceInspection,SqlDialectInspection
func (orm *SysDeptDaoImpl) CheckDeptByIdAndName(ctx context.Context, deptId int64, deptName string) (bool, error) {
	var count int64
	if err := orm.db.WithContext(ctx).Model(&model.Dept{}).
		Where("dept_name = ? AND id != ?", deptName, deptId).
		Count(&count).Error; err != nil {
		return false, errors.New("通过ID与名称查询查询部门失败")
	}
	return count > 0, nil
}

func (orm *SysDeptDaoImpl) SelectListByDept(ctx context.Context, dept *model.Dept) ([]*model.Dept, error) {
	var deptList []*model.Dept

	db := orm.db.WithContext(ctx).Model(&model.Dept{})
	if dept == nil {
		db.Where("del_flag = ?", "0")
	} else {
		if dept.DeptStatus != "" {
			db.Where("dept_status = ?", dept.DeptStatus)
		}

		if dept.DeptName != "" {
			db.Where("dept_name ILIKE CONCAT('%' ||?|| '%')", dept.DeptName)
		}
	}

	if err := db.Find(&deptList).Error; err != nil {
		zerolog.DefaultContextLogger.Error().Err(err).Msg("通过key查询配置错误===")
		return nil, errors.New("通过部门查询部门失败")
	}
	return deptList, nil
}

func (orm *SysDeptDaoImpl) BuildDeptTree(list []*model.Dept) ([]*model.Dept, error) {
	var deptList []*model.Dept
	var deptIdList []any
	for _, dept := range list {
		deptIdList = append(deptIdList, dept.Id)
	}

	for _, dept := range list {
		if utils.SliceContains(deptIdList, dept.ParentId) {
			resourceFN(list, dept)
			deptList = append(deptList, dept)
		}
	}

	if len(deptIdList) == 0 {
		deptList = list
	}

	return deptList, nil
}

func (orm *SysDeptDaoImpl) BuildDeptTreeSelect(list []*model.Dept) []*model.TreeSelect {

	var allList []*model.Dept

	if err := orm.db.WithContext(context.Background()).Where("dept_status = ?", "0").Find(&allList).Error; err != nil {
		zerolog.DefaultContextLogger.Error().Err(err).Msg("获取父节点错误===")
	}

	return BuildDeptTree(allList)
}

func BuildDeptTree(deptSlice []*model.Dept) []*model.TreeSelect {
	// 第一步：建立ID到部门的映射
	deptMap := make(map[int64]*model.Dept)
	for _, d := range deptSlice {
		deptMap[d.Id] = d
	}

	// 第二步：构建树形结构
	var roots []*model.Dept
	for _, d := range deptSlice {
		parentId := d.ParentId
		if parentId == 0 {
			roots = append(roots, d)
			continue
		}

		// 将当前节点挂载到父节点下
		if parent, exists := deptMap[parentId]; exists {
			parent.Children = append(parent.Children, d)
		}
	}

	var treeSelectList []*model.TreeSelect
	for _, dept := range roots {
		treeSelect := model.NewTreeSelectByDept(dept)
		treeSelectList = append(treeSelectList, treeSelect)
	}

	return treeSelectList
}

//goland:noinspection SqlResolve,SqlCaseVsIf,SqlNoDataSourceInspection,SqlDialectInspection
func (orm *SysDeptDaoImpl) SelectListByRoleId(ctx context.Context, roleId int64) ([]int64, error) {
	roleInfo, err := orm.RoleDao.SelectById(ctx, roleId)
	if err != nil {
		zerolog.DefaultContextLogger.Error().Err(err).Msg("获取用户角色错误===")
		return nil, errors.New("获取用户角色失败")
	}

	if roleInfo == nil {
		return nil, errors.New("角色信息不存在")
	}

	var deptIds []int64

	var (
		querySql string
		args     = []any{roleInfo.Id} // 默认第一个参数是 role_id
	)

	switch roleInfo.DeptCheckStrictly {
	case "0":
		querySql = `SELECT
						d.id
					FROM
						sys_dept d
						LEFT JOIN sys_role_dept rd ON d.id = rd.dept_id
					WHERE
						rd.role_id = ?`
	case "1":

		querySql = `SELECT
							d.id
						FROM
							sys_dept d
							LEFT JOIN sys_role_dept rd ON d.id = rd.dept_id
						WHERE
							rd.role_id = ?
							AND d.id NOT IN (
								SELECT
									d.parent_id
								FROM
									sys_dept d
									INNER JOIN sys_role_dept rd ON d.id = rd.dept_id
									AND rd.role_id = ?
							)`
		args = append(args, roleInfo.Id)
	}
	if err = orm.db.WithContext(ctx).Raw(querySql, args...).Find(&deptIds).Error; err != nil {
		zerolog.DefaultContextLogger.Error().Err(err).Msg("通过角色查询部门错误===")
		return nil, errors.New("通过角色查询部门失败")
	}

	return deptIds, nil
}

func (orm *SysDeptDaoImpl) SelectAll(ctx context.Context) ([]*model.Dept, error) {
	var (
		list    []*model.Dept
		allData []*model.Dept
		count   int64
		page    = 1
		size    = 10000
	)
	db := orm.db.WithContext(ctx).Model(&model.Dept{})
	if err := db.Count(&count).Error; err != nil {
		zerolog.DefaultContextLogger.Error().Err(err).Msg("查询部门总条数错误")
		return nil, errors.New("查询部门总条数失败")
	}
	zerolog.DefaultContextLogger.Info().Msgf("获取部门分页===%d,当前分页批数===%d", count, size)

	// 分页大小大于或等于总条数，则直接一次性查询
	if int64(size) >= count {
		if err := db.Find(&allData).Error; err != nil {
			return nil, err
		}
		zerolog.DefaultContextLogger.Info().Msgf("分页大小大于或等于总条数一次性查询所有数据的条数===%d", len(allData))
		return allData, nil
	}

	for {
		list = list[:0]
		if err := db.Limit(size).Offset((page - 1) * size).Find(&list).Error; err != nil {
			return nil, err
		}
		zerolog.DefaultContextLogger.Info().Msgf("第%d次查询部门分页数据条数===%d", page, len(list))

		if len(list) == 0 {
			zerolog.DefaultContextLogger.Info().Msgf("第%d次查询部门分页数据条数为0退出循环", page)
			break
		}
		allData = append(allData, list...)
		page++
	}
	return allData, nil
}

func (orm *SysDeptDaoImpl) SelectByIds(ctx context.Context, ids []int64) (map[int64]string, error) {
	var deptSlice []*model.Dept
	if err := orm.db.WithContext(ctx).Select("id, dept_name").Find(&deptSlice, ids).Error; err != nil {
		return nil, err
	}
	// 部门ID与名称的Map
	deptMap := make(map[int64]string, 0)
	if len(deptSlice) > 0 {
		for _, dept := range deptSlice {
			deptMap[dept.Id] = dept.DeptName
		}
	}
	return deptMap, nil
}

func resourceFN(list []*model.Dept, dept *model.Dept) {
	childList := GetChildList(list, dept)
	dept.Children = childList
	for _, tChild := range childList {
		if HasChild(list, tChild) {
			resourceFN(list, tChild)
		}
	}
}

func GetChildList(list []*model.Dept, dept *model.Dept) []*model.Dept {
	var tList []*model.Dept
	for _, tc := range list {
		if tc.ParentId != 0 && tc.ParentId == dept.Id {
			tList = append(tList, tc)
		}
	}
	return tList
}

func HasChild(list []*model.Dept, dept *model.Dept) bool {
	return len(GetChildList(list, dept)) > 0
}
